/**
 * Dianping.com Inc.
 * Copyright (c) 2003-2013 All Rights Reserved.
 */
package com.dianping.pigeon.remoting.invoker.route.balance;

import java.util.List;
import java.util.Random;

import com.dianping.pigeon.log.Logger;

import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.invoker.Client;
import com.dianping.pigeon.remoting.invoker.config.InvokerConfig;
import com.dianping.pigeon.remoting.invoker.route.statistics.ServiceStatisticsHolder;
import com.dianping.pigeon.util.LangUtils;

public class WeightedAutoawareLoadBalance extends AbstractLoadBalance {

    private static final Logger logger = LoggerLoader.getLogger(WeightedAutoawareLoadBalance.class);
    public static final String NAME = "weightedAutoaware";
    public static final LoadBalance instance = new WeightedAutoawareLoadBalance();
    private static int defaultFactor = ConfigManagerLoader.getConfigManager().getIntValue(
            "pigeon.loadbalance.defaultFactor", 100);
    private static Random random = new Random();

    @Override
    public Client doSelect(List<Client> clients, InvokerConfig<?> invokerConfig, InvocationRequest request,
                           int[] weights) {
        assert (clients != null && clients.size() >= 1);
        if (clients.size() == 1) {
            return clients.get(0);
        }
        float minCapacity = Float.MAX_VALUE;
        int clientSize = clients.size();
        Client[] candidates = new Client[clientSize];
        int candidateIdx = 0;
        for (int i = 0; i < clientSize; i++) {
            Client client = clients.get(i);
            float capacity = ServiceStatisticsHolder.getCapacity(client.getAddress());
            if (logger.isDebugEnabled()) {
                logger.debug("capacity:" + LangUtils.toString(capacity, 4) + ", weight:" + weights[i] + " for address:"
                        + client.getAddress());
            }

            // 根据权重重新调整请求容量
            if (weights[i] > 0) {
                capacity /= weights[i];
            } else {
                capacity = Float.MAX_VALUE;
            }

            if (capacity < minCapacity) {
                minCapacity = capacity;
                candidateIdx = 0;
                candidates[candidateIdx++] = client;
            } else if (Math.abs(capacity - minCapacity) < 1e-6) {
                candidates[candidateIdx++] = client;
            }
        }
        Client client = candidateIdx == 1 ? candidates[0] : candidates[random.nextInt(candidateIdx)];
        if (logger.isDebugEnabled()) {
            logger.debug("select address:" + client.getAddress());
        }
        return client;
    }

}
